/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package logic.optimization;

import data.EntityContainer;
import data.model.Airline;
import data.model.Airplane;
import data.model.Airport;
import data.model.Flight;
import data.model.Place;
import data.model.Route;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 *
 * @author Petr
 * 
 * optimalizace letovych tras se provede pomoci metody optimizeRoutes()
 * vypis novych letovych tras se provede pomoci metody getNewRoutesInfo()
 */
public class Optimizer {

    private EntityContainer entityContainer;
    private List<Airplane> airplanes;
    private List<Airline> airlines;
    private List<Airport> airports;
    private List<Flight> flights;
    private List<Place> places;
    private List<Route> routes;
    private List<Route> newRoutesByCSA = new ArrayList<Route>();
    private List<Route> newRoutesByAnyone = new ArrayList<Route>();

    private Optimizer() {
    }

    public static Optimizer getInstance() {
        return OptimizerHolder.INSTANCE;
    }

    private void initialize() {
        airplanes = entityContainer.getAirplanes();
        airlines = entityContainer.getAirlines();
        airports = entityContainer.getAirports();
        flights = entityContainer.getFlights();
        places = entityContainer.getPlaces();
        routes = entityContainer.getRoutes();
    }

    private static class OptimizerHolder {

        private static final Optimizer INSTANCE = new Optimizer();
    }

    public void optimizeRoutes(EntityContainer entityContainer) {
        this.entityContainer = entityContainer;
        initialize();
        optimizeRoutes();
    }

    private void optimizeRoutes() {
        newRoutesByCSA.clear();
        newRoutesByAnyone.clear();
        for (Place place : places) {
            calculateImportance(place);
        }
        for (Route route : routes) {
            route.setRating(getRouteImportance(route.getDepartureAirport().getPlace(), route.getArrivalAirport().getPlace()));
            route.setFrequency(getFlightsByRoute(route).size());
            route.setWeeklyPassengers(getCapacityByRoute(route));
        }

        for (Place placeDeparture : places) {
            for (Place placeArrival : places) {
                if (!placeDeparture.equals(placeArrival) && getRouteImportance(placeDeparture, placeArrival) > 30) {
                    if (!isRouteByCSA(placeDeparture, placeArrival)) {
                        Route newRoute = getRouteByAirports(placeDeparture, placeArrival);
                        if (newRoute != null) {
                            newRoutesByCSA.add(newRoute);
                        }
                    }
                }
            }
        }
        routes.addAll(newRoutesByAnyone);
        saveOptimalizationResult();
    }

    private void saveOptimalizationResult() {
        entityContainer.setPlaces(places);
        entityContainer.setRoutes(routes);
        entityContainer.setAirlines(airlines);
        entityContainer.setAirplanes(airplanes);
        entityContainer.setFlights(flights);
        entityContainer.setAirports(airports);
    }

    public List<String> getNewRoutesInfo() {
        String info;
        List<String> infos = new ArrayList<String>();
        for (Route route : newRoutesByAnyone) {
            info = "Nova linka " + route.getDepartureAirport().getName() + "-" + route.getArrivalAirport().getName() + " ma rating "
                    + route.getRating();
            infos.add(info);
        }
        for (Route route : newRoutesByCSA) {
            info = "Nova linka " + route.getDepartureAirport().getName() + "-" + route.getArrivalAirport().getName() + " ma rating "
                    + route.getRating() + ", pusobi na ni " + getAirlinesByRoute(route).size() + " spolecnosti, ktere prepravi "
                    + getCapacityByRoute(route) + " lidi tydne.";
            infos.add(info);

        }

        return infos;
    }

    public EntityContainer getEntityContainer() {
        entityContainer.setRoutes(routes);
        return entityContainer;
    }

    private int getRouteImportance(Place from, Place where) {
        if (from.getImportance() < 20 || where.getImportance() < 20) {
            return (from.getImportance() + where.getImportance()) - 20;
        }
        return (from.getImportance() + where.getImportance());

    }

    private Route isRoute(Place from, Place where) {//vrati trasu z A do B

        for (Route route : routes) {
            if (from.getId().compareTo(route.getDepartureAirport().getPlace().getId()) == 0
                    && where.getId().compareTo(route.getArrivalAirport().getPlace().getId()) == 0) {
                return route;
            }
        }
        Route r = new Route();
        r.setDepartureAirport(getBestAirportByPlace(from));
        System.out.println(where.getName());
        r.setRating(getRouteImportance(r.getDepartureAirport().getPlace(), r.getArrivalAirport().getPlace()));
        r.setWeeklyPassengers(0);
        r.setFrequency(0);
        newRoutesByAnyone.add(r);

        return null;
    }

    private boolean isRouteByCSA(Place from, Place where) {//je trasa z A do B spravovana CSA?
        Route r = isRoute(from, where);
        if (r != null) {
            for (Flight flight : flights) {
                if (flight.getAirline().getName().compareTo("CSA") == 0
                        && flight.getRoute().getDepartureAirport().getId().compareTo(r.getDepartureAirport().getId()) == 0
                        && flight.getRoute().getArrivalAirport().getId().compareTo(r.getArrivalAirport().getId()) == 0) {
                    return true;
                }
            }
        }
        return false;

    }

    private Airport getBestAirportByPlace(Place place) {
        Airport bestAirport = new Airport();
        int help = 0;
        for (Airport airport : airports) {
            if (airport.getPlace().getName().compareTo(place.getName()) == 0) {
                if (airport.getAnnual_number_of_passengers() > help) {
                    bestAirport = airport;
                    help = airport.getAnnual_number_of_passengers();
                }
            }
        }
        System.out.println(bestAirport.getName());
        return bestAirport;
    }

    private List<Route> getRoutesByAirport(Airport a) {//trasy ktere vedou pres letiste
        List<Route> routesByAirport = new ArrayList<Route>();
        for (Route route : routes) {
            if (route.getDepartureAirport().getId().compareTo(a.getId()) == 0) {
                routesByAirport.add(route);
            }
        }
        return routesByAirport;
    }

    private Route getRouteByAirports(Place departure, Place arrival) {
        for (Route route : routes) {
            if (route.getDepartureAirport().getPlace().getId().compareTo(departure.getId()) == 0
                    && route.getArrivalAirport().getPlace().getId().compareTo(arrival.getId()) == 0) {
                return route;
            }
        }
        return null;
    }

    private int routesCount(Airport a) {//pocet tras ktere vedou pres letiste
        return getRoutesByAirport(a).size();
    }

    //rocni pocet lidi ktere odbavi letiste
    private int yearlyPassengers(Airport a) {
        return a.getAnnual_number_of_passengers();
    }

    private List<Flight> getFlightsByRoute(Route route) {
        List<Flight> flightsByRoute = new ArrayList<Flight>();
        for (Flight flight : flights) {
            if (route.getId() != null && route.getId().compareTo(flight.getRoute().getId()) == 0) {
                flightsByRoute.add(flight);
            }
        }
        return flightsByRoute;
    }

    private Set<Airline> getAirlinesByRoute(Route route) {
        List<Flight> flightsByRoute = getFlightsByRoute(route);
        Set<Airline> airlinesByRoute = new HashSet<Airline>();
        for (Flight flight : flightsByRoute) {
            airlinesByRoute.add(flight.getAirline());
        }
        return airlinesByRoute;
    }

    private int getPopulationIndex(int population) {
        int populationIndex = 0;
        if (population < 100000) {
            populationIndex = 5;
        } else if (population < 500000) {
            populationIndex = 10;
        } else if (population < 1000000) {
            populationIndex = 15;
        } else if (population < 2000000) {
            populationIndex = 20;
        } else if (population < 3000000) {
            populationIndex = 25;
        } else if (population < 5000000) {
            populationIndex = 30;
        } else if (population > 5000000) {
            populationIndex = 35;
        }
        return populationIndex;
    }
// nevim jak ziskat celkovej pocet linek z danyho letiste
//jelikoz se odtamtad muze litat do celyho sveta a my delame jen evropu
   /* private int getRoutesIndex(int numberOfRoutes) {
    int routesIndex = 0;
    if (numberOfRoutes < 50) {
    routesIndex = 5;
    } else if (numberOfRoutes < 100) {
    routesIndex = 10;
    } else if (numberOfRoutes < 200) {
    routesIndex = 15;
    } else if (numberOfRoutes < 300) {
    routesIndex = 20;
    }
    return routesIndex;
    }
     */

    private int getCapacityByRoute(Route r) {
        int capacity = 0;
        for (Flight flight : flights) {
            if (flight.getRoute().equals(r)) {
                capacity += flight.getAirplane().getSeats();
            }
        }
        return capacity;
    }

    private int getPassengersIndex(int yearlyPassengers) {
        return yearlyPassengers / 1000000;
    }

    private void calculateImportance(Place place) {
        int populationIndex = getPopulationIndex(place.getPopulation());
        int passengersIndex;
        int totalNumberOfRoutes = 0;
        int yearlyPassengers = 0;
        int importance;
        List<Airport> airportsPlace = new ArrayList<Airport>();
        for (Airport airport : airports) {
            if (airport.getPlace() == place) {
                airportsPlace.add(airport);//letiste ktera patri mestu
            }
        }
        for (Airport airport : airportsPlace) {
            totalNumberOfRoutes += routesCount(airport);//celkovy pocet tras ve meste
            yearlyPassengers += yearlyPassengers(airport);//celkovy pocet odbavenych pasazeru
        }
        passengersIndex = getPassengersIndex(yearlyPassengers);//index ukazujici pocet prepravenych pasezeru
        importance = passengersIndex + populationIndex;
        place.setImportance(importance);
    }
}
